[SEA-NodeJS] (1/8) Backend abstraction — IBackend / ISessionBackend / IOperationBackend#378
[SEA-NodeJS] (1/8) Backend abstraction — IBackend / ISessionBackend / IOperationBackend#378msrathore-db wants to merge 3 commits into
Conversation
…kend Refactors DBSQLClient/Session/Operation to dispatch through three backend interfaces. ThriftBackend (lib/thrift-backend/) contains the relocated existing thrift logic. SeaBackend (lib/sea/) is a stub for M0; the sea-napi-binding feature wires the real impl. Public surface (lib/index.ts) unchanged. No new dependencies. All existing tests pass. Files: - lib/contracts/IBackend.ts (new) - lib/contracts/ISessionBackend.ts (new) - lib/contracts/IOperationBackend.ts (new) - lib/contracts/IDBSQLClient.ts (adds useSEA?: boolean to ConnectionOptions) - lib/thrift-backend/ThriftBackend.ts (new) - lib/thrift-backend/ThriftSessionBackend.ts (new) - lib/thrift-backend/ThriftOperationBackend.ts (new) - lib/sea/SeaBackend.ts (new, M0 stub) - lib/DBSQLClient.ts (dispatch through IBackend; useSEA picks SeaBackend) - lib/DBSQLSession.ts (facade over ISessionBackend; staging stays here) - lib/DBSQLOperation.ts (facade over IOperationBackend; iterators/fetchAll stay here) - tests/unit/DBSQLClient.test.ts (retarget internal state lookup through backend; pre-seed client.backend in tests that bypass connect()) - tests/unit/DBSQLOperation.test.ts (retarget internal state lookup through backend)
…nline type Addresses code-bloat-watchdog findings from commit 0085928: - Restores public-API JSDoc on DBSQLSession + DBSQLOperation methods (was deleted as scope creep; contracts unchanged so docs still apply) - Adds makeStubbedClient() helper to tests/unit/DBSQLClient.test.ts; replaces 14× duplicated ThriftBackend pre-seed - Imports WaitUntilReadyOptions instead of inline option types in IOperationBackend + DBSQLOperation.waitUntilReady
|
Thanks for your contribution! To satisfy the DCO policy in our contributing guide every commit message must include a sign-off message. One or more of your commits is missing this message. You can reword previous commit messages with an interactive rebase ( |
Round-N fixes from the 9-reviewer pre-review. Public IOperation/DBSQLOperation
surface preserved byte-identical; backend interfaces (IBackend / ISessionBackend
/ IOperationBackend) made fully neutral so both Thrift and SEA can implement
the same contract.
F1 — neutral DTOs at IOperationBackend with Thrift-shape preservation on the
public facade (adapter pattern):
- lib/contracts/OperationStatus.ts (new) — neutral OperationStatus + OperationState
enum mirroring databricks-sql-python's CommandState and kernel pyo3's
StatementStatus taxonomy.
- lib/contracts/ResultMetadata.ts (new) — neutral ResultMetadata + ResultFormat
enum mirroring the three TSparkRowSetType cases.
- IOperationBackend.status()/getResultMetadata() return the neutral DTOs.
- ThriftOperationBackend.status() adapts at the boundary via adaptOperationStatus
/ adaptResultMetadata; module-level helpers thriftStateToOperationState and
thriftRowSetTypeToResultFormat do the enum maps.
- ThriftOperationBackend exposes thriftStatusResponse() and
thriftResultMetadataResponse() as public Thrift-only accessors used by the
facade's zero-loss fast path (kept for internal state-machine + result-handler
dispatch as well).
- lib/utils/thriftWireSynthesis.ts (new) — synthesizeThriftStatus and
synthesizeThriftResultSetMetadata: convert neutral DTOs back to Thrift wire
shape for the non-Thrift backend path. Lossy on Thrift-only fields
(taskStatus, numModifiedRows, cacheLookupResult, etc.).
- DBSQLOperation.status() and getMetadata() preserved Thrift return shape:
Thrift backend path returns the real wire response (zero loss); non-Thrift
backend path synthesizes via the new helpers.
- DBSQLOperation.getResultMetadata() — new additive neutral accessor on
IOperation; DBSQLSession.handleStagingOperation uses it instead of the
deprecated Thrift-shaped getMetadata().
F2 — IBackend.connect() is now zero-arg. Backend reads everything it needs
from IClientContext / constructor; matches Python connector's pattern of
passing session_configuration via constructor not method-arg.
F3 — Restore the 'Server protocol version' debug log dropped by the original
PR-378 refactor. Re-added to ThriftSessionBackend.constructor with the
LogLevel.debug + IClientContext.getLogger() pattern; matches the pre-refactor
log site at main:lib/DBSQLSession.ts:175.
F4 + F11 + F14 — SeaBackend stub safety:
- close() is a no-op so DBSQLClient.close()'s state-clearing block can finish
even after a useSEA: true connect() failure.
- connect() and openSession() throw HiveDriverError instead of generic Error,
matching the rest of the codebase.
- connect(options: ConnectionOptions) and openSession(request: OpenSessionRequest)
declare their parameters (with @typescript-eslint/no-unused-vars disable)
so IDE autocomplete prompts the M1 SEA implementer.
F6 + F7 + F9 + F10 — JSDoc on backend interfaces:
- IBackend: connect/openSession/close docstrings; close() doc explicitly
states transport-layer cleanup is owned by DBSQLClient.
- ISessionBackend: copy IDBSQLSession's per-method one-liner JSDoc.
- IOperationBackend: doc hasResultSet (readonly external; mutates internally),
waitUntilReady (MUST throw OperationStateError on terminal non-success).
F8 — tests/unit/sea/SeaBackend.test.ts (new) locks in the stub contract:
connect() rejects HiveDriverError, openSession() rejects HiveDriverError,
close() resolves no-op. ~30 LOC.
F12 — Drop legacy { handle, ... } ctor branch from DBSQLOperation and
DBSQLSession:
- Facades accept only { backend, context }.
- DBSQLSession no longer imports ThriftSessionBackend at all.
- DBSQLOperation imports ThriftOperationBackend solely for the F1 typed
downcast (zero-loss Thrift fast path); this is a deliberate, scoped
coupling tied to the back-compat decision.
- tests/unit/.stubs/createSessionForTest.ts and createOperationForTest.ts
(new) wrap the legacy shape; all 48 + 54 test sites mechanically migrated.
F15 — ThriftOperationBackend.waitUntilReady uses imported WaitUntilReadyOptions
type instead of an anonymous inline shape.
F16 — useSEA flag moved out of public ConnectionOptions:
- Removed useSEA?: boolean from the exported lib/contracts/IDBSQLClient.ts
ConnectionOptions; no longer ships in the public .d.ts.
- lib/contracts/InternalConnectionOptions.ts (new) declares the flag as a
non-exported internal extension; DBSQLClient.connect() reads via a typed
cast. Mirrors Python's kwargs.get('use_sea', False) pattern at
databricks-sql-python/src/databricks/sql/session.py:111.
F17 — Missing return; after case 'timeout' in forwardConnectionEvent so a
future fifth case doesn't silently fall through. The trailing return; in
the last case triggers no-useless-return — quieted with a localized
eslint-disable-next-line + intent comment.
F5 — deferred per owner instruction (test-only as any cast tightening).
Verification:
- yarn lint clean (3 pre-existing warnings in tests/e2e/protocol_versions.test.ts).
- yarn build clean.
- tsc --noEmit -p tsconfig.json clean (apart from pre-existing
examples/tokenFederation/* import errors that exist on main).
- Runtime smoke test of SeaBackend stub + Thrift-wire synthesis round-trip
passes 5/5 assertions.
Co-authored-by: Isaac
Signed-off-by: Madhavendra Rathore <madhavendra.rathore@databricks.com>
Addresses PR #380 review findings C1, C2 (partial), C3, H1, H2, H3, H4, H5, H6, H7, H8 (runtime guard), H9, M1, M2, M3, M4 (linguist), M5, M6, M7, M8, L1, L2, L4. - C1 lint: drop `.js` ext from native/sea require so eslint import/extensions passes; gitignore the auto-generated index.js and *.node artifacts; prettier-ignore the napi-rs auto-generated index.d.ts / index.js. - C2 publish: whitelist native/sea/index.{js,d.ts} in .npmignore; declare @databricks/sea-native-linux-x64-gnu in optionalDependencies. The kernel-side package-name rename (drop the linux-x64-gnu prefix) is tracked separately as a cross-PR ask. - C3 test wiring: move tests/native/version.test.ts -> tests/unit/sea/, tests/native/e2e-smoke.test.ts -> tests/e2e/sea/; both now picked up by the existing mocharc globs and run via the existing `npm test` / `npm run e2e` jobs. - H1 noise drop: version.test.ts now asserts one meaningful semver check; three tautological assertions removed. - H2 + H3 + H7 lazy load: rewrite SeaNativeLoader.ts on the lib/utils/lz4.ts pattern. Lazy require behind getSeaNative(); capability-detection helper tryGetSeaNative(); structured error messages that classify MODULE_NOT_FOUND vs ERR_DLOPEN_FAILED and include platform/arch/Node-version + install hint. - H4 supply chain: pin @napi-rs/cli to 2.18.4 in devDependencies; build:native switches to `npx --no-install` so the pinned local install is used (no per-build network fetch). - H5 path: switch build:native default kernel path to the canonical `../../databricks-sql-kernel/napi-binding` (the worktree-specific `-sea-WT` suffix is gone). - H6 CI safety: e2e suite hard-fails when CI=true and any required env var is missing; dev machines still skip. - H8 runtime guard: loader throws a structured error on Node <18 instead of attempting a dlopen that would fail mysteriously. Driver's engines.node stays >=14 — Thrift consumers on older Node continue to work. - H9 + M1 + M2 types: tsconfig adds a `@sea-native` path alias to native/sea/index.d.ts; loader imports the real Connection / Statement / ConnectionOptions / ExecuteOptions / ArrowBatch / ArrowSchema types and re-exports them. Tests use the re-exported types — the inline-shape duplication across three files collapses. - M3 README: rewrite native/sea/README.md to match kernel reality. The napi crate is a standalone Cargo workspace (not a pyo3 sibling); explain the tls-rustls choice that the standalone workspace exists to enable. - M4 drift detection: mark native/sea/index.d.ts as linguist-generated in .gitattributes so GitHub collapses it in diffs and excludes from blame/language stats. - M5 artifacts: gitignore native/sea/index.js, index.node, index.*.node. - M6 + M7 e2e coverage: decode the IPC payload via apache-arrow's tableFromIPC and assert numRows + cell value (not just shape); add drain-past-null idempotence and schema-before-fetch coverage. - L1 build scripts: collapse build:native + build:native:debug into one script taking BUILD_PROFILE (defaults to --release). - L2 / L4 / M8: drop the version() alias and the "Round 2+ will…" comment debt; the loader now actually delivers the value the prior JSDoc was only promising. Verified on this branch: npm run lint clean (0 errors); npm run prettier clean for PR-owned files (3 unrelated pre-existing warnings on PR #378 territory); tsc --project tsconfig.build.json clean; mocha on tests/unit/sea passes (4/4); mocha on tests/e2e/sea passes against a live pecotesting warehouse (2/2, IPC decode confirms SELECT 1 returns 1).
Addresses PR #380 review findings C1, C2 (partial), C3, H1, H2, H3, H4, H5, H6, H7, H8 (runtime guard), H9, M1, M2, M3, M4 (linguist), M5, M6, M7, M8, L1, L2, L4. - C1 lint: drop `.js` ext from native/sea require so eslint import/extensions passes; gitignore the auto-generated index.js and *.node artifacts; prettier-ignore the napi-rs auto-generated index.d.ts / index.js. - C2 publish: whitelist native/sea/index.{js,d.ts} in .npmignore; declare @databricks/sea-native-linux-x64-gnu in optionalDependencies. The kernel-side package-name rename (drop the linux-x64-gnu prefix) is tracked separately as a cross-PR ask. - C3 test wiring: move tests/native/version.test.ts -> tests/unit/sea/, tests/native/e2e-smoke.test.ts -> tests/e2e/sea/; both now picked up by the existing mocharc globs and run via the existing `npm test` / `npm run e2e` jobs. - H1 noise drop: version.test.ts now asserts one meaningful semver check; three tautological assertions removed. - H2 + H3 + H7 lazy load: rewrite SeaNativeLoader.ts on the lib/utils/lz4.ts pattern. Lazy require behind getSeaNative(); capability-detection helper tryGetSeaNative(); structured error messages that classify MODULE_NOT_FOUND vs ERR_DLOPEN_FAILED and include platform/arch/Node-version + install hint. - H4 supply chain: pin @napi-rs/cli to 2.18.4 in devDependencies; build:native switches to `npx --no-install` so the pinned local install is used (no per-build network fetch). - H5 path: switch build:native default kernel path to the canonical `../../databricks-sql-kernel/napi-binding` (the worktree-specific `-sea-WT` suffix is gone). - H6 CI safety: e2e suite hard-fails when CI=true and any required env var is missing; dev machines still skip. - H8 runtime guard: loader throws a structured error on Node <18 instead of attempting a dlopen that would fail mysteriously. Driver's engines.node stays >=14 — Thrift consumers on older Node continue to work. - H9 + M1 + M2 types: tsconfig adds a `@sea-native` path alias to native/sea/index.d.ts; loader imports the real Connection / Statement / ConnectionOptions / ExecuteOptions / ArrowBatch / ArrowSchema types and re-exports them. Tests use the re-exported types — the inline-shape duplication across three files collapses. - M3 README: rewrite native/sea/README.md to match kernel reality. The napi crate is a standalone Cargo workspace (not a pyo3 sibling); explain the tls-rustls choice that the standalone workspace exists to enable. - M4 drift detection: mark native/sea/index.d.ts as linguist-generated in .gitattributes so GitHub collapses it in diffs and excludes from blame/language stats. - M5 artifacts: gitignore native/sea/index.js, index.node, index.*.node. - M6 + M7 e2e coverage: decode the IPC payload via apache-arrow's tableFromIPC and assert numRows + cell value (not just shape); add drain-past-null idempotence and schema-before-fetch coverage. - L1 build scripts: collapse build:native + build:native:debug into one script taking BUILD_PROFILE (defaults to --release). - L2 / L4 / M8: drop the version() alias and the "Round 2+ will…" comment debt; the loader now actually delivers the value the prior JSDoc was only promising. Verified on this branch: npm run lint clean (0 errors); npm run prettier clean for PR-owned files (3 unrelated pre-existing warnings on PR #378 territory); tsc --project tsconfig.build.json clean; mocha on tests/unit/sea passes (4/4); mocha on tests/e2e/sea passes against a live pecotesting warehouse (2/2, IPC decode confirms SELECT 1 returns 1). Signed-off-by: Madhavendra Rathore <madhavendra.rathore@databricks.com>
| disableBuffering: options?.disableBuffering, | ||
| }); | ||
| const limit = options?.maxRows ?? defaultMaxRows; | ||
| const result = await this.backend.fetchChunk({ limit, disableBuffering: options?.disableBuffering }); |
There was a problem hiding this comment.
High — behavioral regression on the Thrift hot path (verified against PR head SHA + main).
Pre-PR fetchChunkInternal sequence:
failIfClosed()waitUntilReady()getResultHandler()← may fetch result-metadata RPCfailIfClosed()← critical mid-flight checksetTimeout(0)macrotask yieldresultHandler.fetchNext()← data RPC (CloudFetch URL download / Arrow batch)failIfClosed()
Post-PR sequence: steps 3–6 now happen inside ThriftOperationBackend.fetchChunk as one atomic block with no failIfClosed check between them. The only intervening macrotask yield (setTimeout(0) at lib/thrift-backend/ThriftOperationBackend.ts:143-145) sits inside that block.
A concurrent cancel()/close() arriving during the yield window will no longer short-circuit the subsequent fetchNext() data RPC. CloudFetch presigned-URL downloads can take seconds, making this observable rather than theoretical.
Suggested fix: add an isClosed: () => boolean callback to IOperationBackend.fetchChunk's options, and have ThriftOperationBackend.fetchChunk check it after setTimeout(0) returns. Alternatively split the metadata-prep phase into a separate backend method so the facade can re-check between them.
Found by: security, performance (independently).
Posted by code-review-squad · /full-review
| * the operation into its closed/cancelled flags; future implementations must | ||
| * use the same error type for the facade to stay in sync. | ||
| */ | ||
| waitUntilReady(options?: WaitUntilReadyOptions): Promise<void>; |
There was a problem hiding this comment.
Medium — locks the cross-backend interface to a Thrift wire shape.
IOperationBackend.waitUntilReady(options?: WaitUntilReadyOptions) is the cross-backend interface, but WaitUntilReadyOptions (imported from ./IOperation) types callback as (progress: TGetOperationStatusResp) => unknown — a Thrift-generated wire type.
Every non-Thrift backend impl must either:
- depend on the thrift IDL just to construct
TGetOperationStatusResp, or - import
synthesizeThriftStatusfromlib/utils/thriftWireSynthesis.tsper-poll, or - skip the user's callback, silently breaking a public contract.
For a Rust kernel polling every ~100 ms during a 10-min query, that's 6,000 synthesized fake Thrift structs purely to satisfy a JS callback signature.
Suggested fix: introduce a separate IOperationBackendWaitOptions { callback?: (status: OperationStatus) => unknown } on the backend interface. DBSQLOperation owns the user's Thrift-typed callback and adapts to/from the neutral one at the facade boundary.
Easier to fix now (one caller, in this PR's own facade) than after the SEA/napi impls land.
Found by: architecture, devils-advocate, security, language.
Posted by code-review-squad · /full-review
| */ | ||
| export function synthesizeThriftStatus(status: OperationStatus): TGetOperationStatusResp { | ||
| return { | ||
| status: synthesizeOkStatus(), |
There was a problem hiding this comment.
Medium — latent correctness bug; bites once any non-Thrift backend is wired in.
synthesizeOkStatus() (line 12) returns { statusCode: TStatusCode.SUCCESS_STATUS } as TStatus unconditionally, and is wired into the status field of the synthesized TGetOperationStatusResp regardless of OperationState.
Existing user code that calls Status.assert(resp.status) after await op.status() against a non-Thrift backend will see SUCCESS_STATUS even when the operation finished in ERROR_STATE or CANCELED_STATE. The lossy-by-design JSDoc comment covers fields like taskStatus/numModifiedRows but does NOT cover status — status is meant to reflect the RPC, but here it's being used as a stand-in for the operation outcome that user code already checks.
Today this is dead code (SEA is a throw-stub); the moment any non-Thrift backend's status() is reachable, error states will silently look successful in the legacy IOperation.status() return value.
Suggested fix: map operation-state to a real TStatus.statusCode:
OperationState.Failed → ERROR_STATUS(and carryerrorMessage/sqlState)OperationState.Cancelled → ERROR_STATUS- other states →
SUCCESS_STATUS
Trivial, prevents a latent correctness gap.
Found by: agent-compat, devils-advocate.
Posted by code-review-squad · /full-review
| @@ -0,0 +1,87 @@ | |||
| import { | |||
There was a problem hiding this comment.
Medium — coverage gap that masks the latent correctness bug in M2.
No test exercises client.connect({ useSEA: true }) end-to-end. The synthesize helpers in this file have no companion tests/unit/utils/thriftWireSynthesis.test.ts. Both DBSQLOperation.status() and DBSQLOperation.getMetadata() have the instanceof ThriftOperationBackend synthesize fallback as dead code from the test suite's perspective.
Any enum-mapping drift (e.g. the M2 issue above) would not be caught by CI.
Suggested fix: add a unit test file tests/unit/utils/thriftWireSynthesis.test.ts covering:
operationStateToThrift: all 7 enum valuesresultFormatToThrift: all 3 enum valuessynthesizeThriftStatus: confirmerrorMessage/sqlStateare preserved; confirmhasResultSetround-trips; confirmstate: Failed⇒ non-SUCCESSTStatus(after M2 fix)synthesizeThriftResultSetMetadata: confirmschema,arrowSchema,isStagingOperation,lz4Compressedare preserved
And one new test in DBSQLClient.test.ts covering the useSEA: true failure path (using as any cast to bypass the InternalConnectionOptions non-exported type).
Found by: test, ops, agent-compat (independently flagged different aspects).
Posted by code-review-squad · /full-review
| private forwardConnectionEvent(event: 'error' | 'reconnecting' | 'close' | 'timeout', payload?: unknown): void { | ||
| switch (event) { | ||
| case 'error': { | ||
| const error = payload as Error; |
There was a problem hiding this comment.
Low — latent crash hazard. Pre-refactor the Thrift EventEmitter typing guaranteed error: Error. Post-refactor payload is unknown and the cast is unchecked. If a future backend ever emits a non-Error value through this path (e.g. a string, which EventEmitter.emit('error', ...) permits), error.stack || error.name + ': ' + error.message will throw TypeError. The try/catch only protects this.emit, not the logger.log line.
- Suggested fix:
const error = payload instanceof Error ? payload : new Error(String(payload));
Posted by code-review-squad · /full-review
| // doesn't ship in the public `.d.ts`. Mirrors Python's `kwargs.get("use_sea")` | ||
| // pattern (see databricks-sql-python/src/databricks/sql/session.py). | ||
| const internalOptions = options as ConnectionOptions & InternalConnectionOptions; | ||
| this.backend = internalOptions.useSEA |
There was a problem hiding this comment.
Low — confusing diagnostic on a partially-initialized path.
With useSEA: true, SeaBackend.connect() always throws (M0 stub), but this.backend is already assigned. A subsequent openSession() passes the !this.backend guard at line 298 and reaches SeaBackend.openSession(), which throws "SEA backend not implemented yet" instead of "DBSQLClient: not connected".
- Suggested fix: either (a) assign
this.backendafter a successfulconnect(), or (b)try/catcharoundawait this.backend.connect(options)and nullthis.backendon failure.
The IBackend.close() JSDoc explicitly mentions "MUST be safe to call on a partially-initialized backend (i.e. after a failed connect())" — but that contract only covers close(), not the openSession-after-failed-connect path.
Posted by code-review-squad · /full-review
| return TSparkRowSetType.ARROW_BASED_SET; | ||
| case ResultFormat.UrlBased: | ||
| return TSparkRowSetType.URL_BASED_SET; | ||
| default: |
There was a problem hiding this comment.
Low — asymmetric with operationStateToThrift above.
operationStateToThrift (lines 16-34) uses an explicit OperationState.Unknown member, making the switch exhaustively-checkable by TS. resultFormatToThrift (lines 36-47) just has a bare default: return COLUMN_BASED_SET. Today the default is dead because ResultFormat is a closed 3-value enum, but if it's extended for SEA (e.g. a new format), the fallback would silently route results through JsonResultHandler and produce garbled data with no error.
- Suggested fix: drop the
default, orthrow new HiveDriverError(...). MatchoperationStateToThrift's pattern.
Posted by code-review-squad · /full-review
| * fields (`cacheLookupResult`, `uncompressedBytes`, `compressedBytes`, | ||
| * `status`) left undefined / defaulted. | ||
| * | ||
| * Prefer {@link DBSQLOperation.getResultMetadata} in new code. |
There was a problem hiding this comment.
Low — agent/IDE discoverability.
The JSDoc says "Prefer DBSQLOperation.getResultMetadata in new code" but does NOT use the @deprecated tag. TypeScript's @deprecated is the canonical signal that IDEs (strikethrough), tsc, ESLint plugins, and agentic codegen pick up. Free-text "Prefer X" won't.
- Suggested fix:
/** * @deprecated Use {@link DBSQLOperation.getResultMetadata}; this method synthesizes Thrift-only fields as `undefined` on non-Thrift backends. */
Posted by code-review-squad · /full-review
|
|
||
| return result; | ||
| } | ||
| // Re-export for back-compat with existing imports. |
There was a problem hiding this comment.
Low — back-compat re-export should signal Thrift-only.
numberToInt64 traffics in node-int64 (a Thrift wire type) and has no place in a backend-neutral facade — it's intentionally re-exported only to preserve back-compat for existing external consumers. Add @deprecated to the re-export and a // Thrift-only JSDoc on the symbol itself, so SDK consumers see the deprecation in their IDE / .d.ts.
Posted by code-review-squad · /full-review
| @@ -211,48 +85,13 @@ export default class DBSQLSession implements IDBSQLSession { | |||
| */ | |||
| public async executeStatement(statement: string, options: ExecuteStatementOptions = {}): Promise<IOperation> { | |||
| await this.failIfClosed(); | |||
There was a problem hiding this comment.
Low — readability / maintenance.
Pre-refactor, handleResponse did one job (re-check open-flag after the backend call returned, because server-side close doesn't error out). The PR removes the helper and inlines a failIfClosed bracket around every backend call — 11 sites with the same shape, no name attached to the "before AND after" semantics.
- Suggested fix:
Then call sites become
private async runBackend<T>(fn: () => Promise<T>): Promise<T> { await this.failIfClosed(); const result = await fn(); await this.failIfClosed(); return result; }
return this.wrapOperation(await this.runBackend(() => this.backend.getCatalogs(request)));. Cuts boilerplate and re-attaches a name to the contract.
Posted by code-review-squad · /full-review
Code Review Squad — ReviewScore: 65/100 — HIGH RISK (score driven by finding count — see notes) The structural refactor is well-scoped and well-documented; the Thrift path is preserved 1:1 for nearly every call site and the 3-tier seam ( The score is depressed by one verified behavioral regression on the Thrift hot path (H1 — Reviewers: 9/9 delivered (security, devils-advocate, language, test, architecture, maintainability, agent-compat, ops, performance). Posted inline
Medium FindingsM3 — Telemetry schema has no
|
Stack
Linear stack of 8 PRs landing the M0 + M1 Phase 1 SEA NodeJS work. Merge in order from base ↑ to tip. The tip branch (
msrathore/sea-auth-u2m, PR #383) is the single snapshot containing everything in flight — point your test or benchmark harness at it for an end-to-end check.sea-abstractionsea-napi-binding.nodeartifactsea-errors-loggingErrorCode→ JS error-class mapping (M0 minimum)sea-authuseSEA: truesea-executionexecuteStatement+openSession(sessionConfig, initialCatalog/Schema)sea-resultssea-operationsea-auth-u2m← TIPCompanion kernel stack (
databricks/databricks-sql-kernel): 8 PRs — root #26 (async-public-api) → #27 → #25 → #29 → #28 → #30 → #24 → #23 (tip).Policy: new PRs always stack on the current tip. No sibling/parallel topology. No force-pushes on existing PRs unless absolutely necessary; if a PR's content is wrong, add a fix-up commit on top of the stack tip rather than rewriting history.
This PR is position 1/8 (ROOT of the stack).
Summary
Foundational backend abstraction — introduces
IBackend/ISessionBackend/IOperationBackendinterfaces and refactors the existing thrift driver to plug in behind them.Size note (1839 LOC, over the 800 cap) — foundational refactor
+1064/-775ratio means ~775 lines are pre-existing code being refactored to plug into the new interfaces (renames, restructuring, no behavior change). Actual review burden is lower than the raw LOC suggests — most of the diff is mechanical file-level refactoring rather than new logic.Cannot be cleanly split: interfaces + at least one implementation (thrift) must land together to compile.
Test plan
IBackendinterfaceSeaBackendcompiles + exposes the same interfaceDraft until you give the go for review.
Tracking
Co-authored-by: Isaac